package loads

import (
	"jvm/nzpJvm/instructions/base"
	"jvm/nzpJvm/runtime_area"
	"jvm/nzpJvm/runtime_area/heap"
)

// 从数组中加载一个引用类型的数据并压入操作数栈。需要两个操作数，第一个数为数组的索引，第二个操作数为数组对象。
// 两个操作数均为从操作数栈弹出
// 指令会将数组指定索引位置上的元素加载到操作数栈中
type A_ALOAD struct{ base.NoOperandsInstruction }
// 加载byte类型数据。其实对boolean数组的加载也是这个指令
type B_ALOAD struct{ base.NoOperandsInstruction }
// 加载char类型数据。
type C_ALOAD struct{ base.NoOperandsInstruction }
type D_ALOAD struct{ base.NoOperandsInstruction }
type F_ALOAD struct{ base.NoOperandsInstruction }
type I_ALOAD struct{ base.NoOperandsInstruction }
type L_ALOAD struct{ base.NoOperandsInstruction }
type S_ALOAD struct{ base.NoOperandsInstruction }

func (self *A_ALOAD) Execute(frame *runtime_area.Frame) {
	stack := frame.OperandStack()
	//  弹出所需的两个操作数
	index := stack.PopInt()
	arrayObject := stack.PopRef()
	// 数组对象不可为空
	checkNotNull(arrayObject)
	// 获取数组对象内部的元素
	refs := arrayObject.Refs()
	// 检查是否存在索引越界
	checkIndex(len(refs), index)
	// 获取元素并且压栈
	stack.PushRef(refs[index])
}

func (self *B_ALOAD) Execute(frame *runtime_area.Frame) {
	stack := frame.OperandStack()
	// 要获取数组哪个索引位置的元素
	index := stack.PopInt()
	//数组对象
	arrayObject := stack.PopRef()
	// 数组对象不可为空
	checkNotNull(arrayObject)
	// 获取数组对象内部的元素
	refs := arrayObject.Bytes()
	// 检查是否存在索引越界
	checkIndex(len(refs), index)
	// 获取元素并且压栈
	stack.PushInt(int32(refs[index]))
}

func (self *C_ALOAD) Execute(frame *runtime_area.Frame) {
	stack := frame.OperandStack()
	// 要获取数组哪个索引位置的元素
	index := stack.PopInt()
	//数组对象
	arrayObject := stack.PopRef()
	// 数组对象不可为空
	checkNotNull(arrayObject)
	// 获取数组对象内部的元素
	refs := arrayObject.Chars()
	// 检查是否存在索引越界
	checkIndex(len(refs), index)
	// 获取元素并且压栈
	stack.PushInt(int32(refs[index]))
}

func (self *D_ALOAD) Execute(frame *runtime_area.Frame) {
	stack := frame.OperandStack()
	// 要获取数组哪个索引位置的元素
	index := stack.PopInt()
	//数组对象
	arrayObject := stack.PopRef()
	// 数组对象不可为空
	checkNotNull(arrayObject)
	// 获取数组对象内部的元素
	refs := arrayObject.Doubles()
	// 检查是否存在索引越界
	checkIndex(len(refs), index)
	// 获取元素并且压栈
	stack.PushDouble(refs[index])
}

func (self *F_ALOAD) Execute(frame *runtime_area.Frame) {
	stack := frame.OperandStack()
	// 要获取数组哪个索引位置的元素
	index := stack.PopInt()
	//数组对象
	arrayObject := stack.PopRef()
	// 数组对象不可为空
	checkNotNull(arrayObject)
	// 获取数组对象内部的元素
	refs := arrayObject.Floats()
	// 检查是否存在索引越界
	checkIndex(len(refs), index)
	// 获取元素并且压栈
	stack.PushFloat(refs[index])
}

func (self *I_ALOAD) Execute(frame *runtime_area.Frame) {
	stack := frame.OperandStack()
	// 要获取数组哪个索引位置的元素
	index := stack.PopInt()
	//数组对象
	arrayObject := stack.PopRef()
	// 数组对象不可为空
	checkNotNull(arrayObject)
	// 获取数组对象内部的元素
	refs := arrayObject.Ints()
	// 检查是否存在索引越界
	checkIndex(len(refs), index)
	// 获取元素并且压栈
	stack.PushInt(refs[index])
}

func (self *L_ALOAD) Execute(frame *runtime_area.Frame) {
	stack := frame.OperandStack()
	// 要获取数组哪个索引位置的元素
	index := stack.PopInt()
	//数组对象
	arrayObject := stack.PopRef()
	// 数组对象不可为空
	checkNotNull(arrayObject)
	// 获取数组对象内部的元素
	refs := arrayObject.Longs()
	// 检查是否存在索引越界
	checkIndex(len(refs), index)
	// 获取元素并且压栈
	stack.PushLong(refs[index])
}

func (self *S_ALOAD) Execute(frame *runtime_area.Frame) {
	stack := frame.OperandStack()
	// 要获取数组哪个索引位置的元素
	index := stack.PopInt()
	//数组对象
	arrayObject := stack.PopRef()
	// 数组对象不可为空
	checkNotNull(arrayObject)
	// 获取数组对象内部的元素
	refs := arrayObject.Shorts()
	// 检查是否存在索引越界
	checkIndex(len(refs), index)
	// 获取元素并且压栈
	stack.PushInt(int32(refs[index]))
}

// 检查给定的索引是否超出了数组的范围
func checkIndex(arrayLength int, index int32) {
	if index < 0 || index >= int32(arrayLength) {
		panic("java.lang.ArrayOutOfBoundsException")
	}
}

// 检查数组对象是否为空
func checkNotNull(object *heap.Object) {
	if object == nil {
		panic("java.lang.NullPointerException")
	}
}
